home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Language/OS - Multiplatform Resource Library
/
LANGUAGE OS.iso
/
cpp_libs
/
answrbok
/
8_5.lha
/
8_5
/
doscan.c
< prev
next >
Wrap
C/C++ Source or Header
|
1993-08-08
|
8KB
|
425 lines
* Copyright (c) 1990 by AT&T Bell Telephone Laboratories, Incorporated. */
* The C++ Answer Book */
* Tony Hansen */
* All rights reserved. */
/ _doscanf(): low level function for doing formatted
/ input, to be invoked by the v*scanf() family
/ of functions.
include <extbuf.h>
include <scanset.h>
include <ctype.h>
include <stdlib.h>
/ get the next character on the input stream,
/ adding to charcnt and returning EOF on end-of-file.
nline int nextc(istream *in, int &charcnt)
char c;
return (!in->get(c)) ?
EOF :
(charcnt++, c);
* Define a macro to decrement the fieldwidth */
* and add the character to the buffer. */
define addc() \
fieldwidth--, svbuf.add(c)
* Define a macro to decrement the fieldwidth, */
* add the character to the buffer and get the */
* next character. */
define Nextc() \
do { addc(); c = nextc(in, charcnt); } while (0)
* Define macros to use for assigning the */
* appropriate value to the appropriate pointer */
* type to be pulled off the stack based on the */
* booleans halfsize, longsize and suppress. */
* assign() checks for halfsize, then invokes */
* liassign() below. */
define assign(var, stype, itype, ltype) \
if (halfsize) \
{ \
stype *p = va_arg(vl, stype*); \
*p = (stype)var; \
} \
\
else \
liassign(var, itype, ltype)
* This macro is invoked from assign() above */
* and for %f, which doesn't allow the halfsized*/
* modifier. It checks for the booleans longsize*/
* and suppress. */
define liassign(var, itype, ltype) \
if (longsize) \
{ \
ltype *p = va_arg(vl, ltype*); \
*p = (ltype)var; \
} \
\
else if (!suppress) \
{ \
itype *p = va_arg(vl, itype*); \
*p = (itype)var; \
}
* define a macro to check for an optional sign */
define checksign() \
if ((fieldwidth > 0) && \
((c == '-') || (c == '+'))) \
{ \
Nextc(); \
}
* This macro will check for one or more */
* characters which have a certain */
* characteristic checked by the testprog, such */
* as isdigit. If the sequence is missing, it */
* is an error and the macro will break out of */
* the switch statement. */
define checkfield(testprog) \
if ((fieldwidth > 0) && testprog(c)) \
do { \
addc(); \
} while ((fieldwidth > 0) && \
testprog(c = nextc(in, charcnt))); \
\
else \
{ \
goodformat = 0; \
break; \
}
* This macro works just like checkfield() */
* except that it is not an error for the */
* sequence to be missing. */
define checkoptfield(testprog) \
for ( ; (fieldwidth > 0) && testprog(c); \
c = nextc(in, charcnt)) \
{ \
addc(); \
}
/ the input function
nt _doscanf(istream *in, const char *fmt, va_list vl)
if (!fmt)
return EOF;
int charcnt = 0;
int fieldcnt = EOF;
int goodformat = 1;
int c = nextc(in, charcnt);
extarray svbuf;
// loop through the format string
while (*fmt && (c != EOF) && goodformat)
{
switch (*fmt++)
{
// format specification
case '%':
{
// '*' means suppress assignment
int suppress = 0;
if (*fmt == '*')
{
suppress = 1;
fmt++;
}
// check for a fieldwidth
int fieldwidth = -1;
if (isdigit(*fmt))
fieldwidth =
(int)strtol(fmt, (char**)&fmt, 10);
// Check for size modifiers.
// Just skip past if assignment
// is being suppressed.
int halfsize = 0;
int longsize = 0;
switch (*fmt)
{
case 'h':
if (!suppress)
halfsize = 1;
fmt++;
break;
case 'l':
if (!suppress)
longsize = 1;
fmt++;
break;
}
// set the default field width
if (fieldwidth == -1)
fieldwidth = (*fmt == 'c') ?
1 : INT_MAX;
// check for skipping white space
switch (*fmt)
{ // these do not skip white space
case '%': case 'n':
case '[': case 'c':
break;
default:
// loop until a non-white-space
// character is found
if (isspace(c))
do {
c = nextc(in, charcnt);
} while (isspace(c));
break;
}
// only return EOF if an error
// occurred before the first
// field specifier is reached
if (fieldcnt == EOF)
fieldcnt = 0;
svbuf.reset();
// check for assignment type
switch (*fmt)
{
// store the number of read chars
case 'n':
assign(charcnt, short, int, long);
break;
// fill svbuf with an integer
// or an unsigned integer
case 'd':
case 'u':
{
checksign();
checkfield(isdigit);
/* assign to a signed number */
if (*fmt == 'd')
{
long i =
strtol(svbuf.val(),
0, 10);
assign(i, short, int, long);
}
/* assign to an unsigned number */
else
{
unsigned long i =
strtoul(svbuf.val(),
0, 10);
assign(i, unsigned short,
unsigned int,
unsigned long);
}
fieldcnt++;
break;
}
// fill svbuf with a C++ integer
case 'i':
{
checksign();
// check for leading 0 or 0x
if ((fieldwidth > 0) &&
(c == '0'))
{
Nextc();
if ((fieldwidth > 0) &&
((c == 'x') ||
(c == 'X')))
{
Nextc();
checkfield(isxdigit);
}
else
checkfield(isodigit);
}
else
checkfield(isdigit);
long i =
strtol(svbuf.val(), 0, 0);
assign(i, short, int, long);
fieldcnt++;
break;
}
// fill svbuf with an octal integer
case 'o':
{
checksign();
checkfield(isodigit);
unsigned long i =
strtoul(svbuf.val(), 0, 8);
assign(i, unsigned short,
unsigned int, unsigned long);
fieldcnt++;
break;
}
// fill svbuf with a hex integer
case 'x': case 'X':
{
checksign();
checkfield(isxdigit);
unsigned long i =
strtoul(svbuf.val(), 0, 16);
assign(i, unsigned short,
unsigned int, unsigned long);
fieldcnt++;
break;
}
// Read in a floating point number
case 'e': case 'E':
case 'g': case 'G':
case 'f':
{
checksign();
checkoptfield(isdigit);
if ((fieldwidth > 0) &&
(c == '.'))
{
Nextc();
checkoptfield(isdigit);
}
if ((fieldwidth > 0) &&
((c == 'e') || (c == 'E')))
{
Nextc();
checksign();
checkfield(isdigit);
}
double i =
strtod(svbuf.val(), 0);
liassign(i, float, double);
fieldcnt++;
break;
}
// Read up to fieldwidth characters
// (minimum of 1) which match the
// scanset. Add a null character at
// the end of the string.
case '[':
{
char *p;
if (c == EOF)
break;
if (!suppress)
p = va_arg(vl, char*);
scanset s(fmt, &fmt);
for ( ; (fieldwidth-- > 0) &&
s.match(c) ;
c = nextc(in, charcnt))
if (!suppress)
*p++ = c;
if (!suppress)
*p = '\0';
fieldcnt++;
break;
}
// Read fieldwidth characters.
// Do NOT add a null character after.
case 'c':
{
char *p;
if (c == EOF)
break;
if (!suppress)
p = va_arg(vl, char*);
for ( ; (fieldwidth-- > 0) &&
(c != EOF) ;
c = nextc(in, charcnt))
if (!suppress)
*p++ = c;
fieldcnt++;
break;
}
// Read a string terminated by
// white-space. Add a null character
// at the end of the string.
case 's':
{
char *p;
if (c == EOF)
break;
if (!suppress)
p = va_arg(vl, char*);
for ( ; (fieldwidth-- > 0) &&
!isspace(c) ;
c = nextc(in, charcnt))
if (!suppress)
*p++ = c;
if (!suppress)
*p = '\0';
fieldcnt++;
break;
}
// match a single %
case '%':
if (c == '%')
c = nextc(in, charcnt);
else
goodformat = 0;
break;
}
break;
}
case ' ': // match white-space
case '\t': // match white-space
if (isspace(c))
do {
svbuf.add(c);
} while
(isspace(c = nextc(in, charcnt)));
else
goodformat = 0;
break;
default: // match character
if (c == fmt[-1])
c = nextc(in, charcnt);
else
goodformat = 0;
break;
}
}
if (c != EOF)
in->putback(c);
return fieldcnt;